#!/bin/env/python
"""Calculates distance and time between two sensors.

Copyright (C) Sarah Mount.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA


The ranger module deals with calculating distances between two points based and time taken to travel between them.
The time calculations are based upon the transmission medium the network is using.

The ranger class has a collection of default mediums and can also be subclassed to provide any transmission medium the user wishes
"""

__author__ = "Sarah Mount"

import math
import random

class Ranger(object):
    """Superclass for all classes implementing ranging facilities.
    Constructor takes coordinates of sending and receiving nodes.
    In Dingo, one pixel represents one cm^2 of space.
    """
    def __init__(self):
        return
    
    def dist(self, (x1, y1), (x2, y2)):
        """Return the Euclidean distance between two points."""
        dist = math.sqrt(math.pow(x2 - x1, 2)+math.pow(y2 - y1, 2))
        return dist

class RFRanger(Ranger):
    """Ranger based on the speed of RF signals"""
    speed_of_em = 30000000000
    """cvar: Speed of light in cm/s."""
    
    def __init__(self):
        super(Ranger, self).__init__()
        return
    def get_rssi(self, (send_x, send_y), (recv_x, recv_y)):
        """Return RSSI for a packet transmission, based on coordinates of sensors.

        @rtype: float
        """
        return 0
    def get_tof(self, (send_x, send_y), (recv_x, recv_y)):
        """Return time of flight for a packet transmission, based on coordinates of sensors.

        @rtype: float
        """
        return 0

class SoundRanger(Ranger):
    """Ranger based on speed of sound"""
    speed_of_sound = 34500
    """cvar: Speed of sound through air in cm/s."""
    
    def __init__(self):
        super(Ranger, self).__init__()
        return


class RFRangerWithLinearNoise(Ranger):
    """Provides a L{Dingo.ranger.RFRanger} with noise.
    This Ranger introduces an amount of random noise into the RSSI and time of
    flight of the broadcast. Time of flight is adjusted by up to 1 millisecond each
    way, any more than this creates huge errors due to the sheer distance the
    signal could have travelled in that time (300km). The time of flight is also
    rounded to a tenth of a millisecond as the sensor will not have a finer timing
    resolution than that usually.
    """
    speed_of_em = 30000000000 
    """cvar: Speed of light in cm/s"""
    
    def __init__(self):
        super(RFRangerWithLinearNoise, self).__init__()
        return

    def get_rssi(self, (send_x, send_y), (recv_x, recv_y)):
        dist = super(RFRangerWithLinearNoise, self).dist((send_x, send_y), (recv_x, recv_y))
        if dist == 0:
            return 1

        randAmount = (random.randrange(19)) - 9
        adjAmount = randAmount / 10000.0
        final = (1 / dist) + adjAmount
        return final

    def get_tof(self, (s_x, s_y), (r_x, r_y)):
        dist = super(RFRangerWithLinearNoise, self).dist((s_x, s_y), (r_x, r_y))
        time = dist / self.speed_of_em                  # Time in seconds
        randAmount = round(random.uniform(-1, 1), 1) / 1000     # This is the number of ms
        time += randAmount
        time = round(time, 4)                       # 10ths of ms
        return time

# class RFRangerWithGaussianNoise(Ranger):
#   speed_of_em = 30000000000 #cm/s through air

#           def __init__(self):
#       super(RFRangerWithGaussianNoise, self).__init__()
#                 return

#   def get_rssi(self, (send_x, send_y), (recv_x, recv_y)):
#       dist = super(RFRangerWithGaussianNoise, self).dist((send_x, send_y), (recv_x, recv_y))
#       if dist == 0:
#           return 1
# #     randAmount = (random.randrange(19)) - 9
#       randAmount = 
#       adjAmount = randAmount / 10000.0
#       final = (1 / dist) + adjAmount
#       return final

#   def get_tof(self, (s_x, s_y), (r_x, r_y)):
#       dist = super(RFRangerWithGaussianNoise, self).dist((s_x, s_y), (r_x, r_y))
#       time = dist / self.speed_of_em                  # Time in seconds
# #     randAmount = round(random.uniform(-1, 1), 1) / 1000     # This is the number of ms
#       randAmount = 
#       time += randAmount
#       time = round(time, 4)                       # 10ths of ms
#       return time


class NaiveRFRanger(Ranger): 
    """Native ranging for RF signals"""
    def __init__(self):
        super(NaiveRFRanger, self).__init__()
        return
    
    def get_rssi(self, (send_x, send_y), (recv_x, recv_y)):
        """Crude estimate of received signal strength.
        """
        dist = super(NaiveRFRanger, self).dist((send_x, send_y), (recv_x, recv_y))
        if dist == 0:
            return 1
        return 1 / dist ### Crude RSSI estimate
    def get_tof(self, (s_x, s_y), (r_x, r_y)):
        """Crude estimate of time of flight in microseconds.
        """
        dist = super(NaiveRFRanger, self).dist((s_x, s_y), (r_x, r_y))
        time = dist / RFRanger.speed_of_em # time in seconds
        return time

class NaiveSoundRanger(SoundRanger):
    """Naive ranging for sound."""
    def __init__(self):
        super(SoundRanger, self).__init__()
        return
    
    def get_new_volume(self, (s_x, s_y), (r_x, r_y), vol):
        """Given the volume of a sound, determine what the volume of
        that sound should be once it has reached its destination.
        """
        dist = super(NaiveSoundRanger, self).dist((s_x, s_y), (r_x, r_y))
        return vol - 1 / (dist * dist)
    
    def get_tof(self, (s_x, s_y), (r_x, r_y)):
        """Crude estimate of time of flight in microseconds."""
        dist = super(NaiveSoundRanger, self).dist((s_x, s_y), (r_x, r_y))
        time = dist / SoundRanger.speed_of_sound # time in seconds
        return time
